package TAIC.feature;
import java.util.*;
import java.io.*; 

public class AssignCluster {
	public static double threshold = 0.7 ;
	int totalCenters = 0 ;
	int DIM = 0 ;
	double 	center [][] = null ;
	
	static public void main ( String str []) throws Exception {
		if ( str.length < 5 ) {
			System.out.println ( "please input:") ;
			System.out.println ( "all, configFile, codeBook, outputfile and threshold (0-1)" ) ;
			System.out.println ( "or one, dataFolder, codeBook, outputfile  and threshold (0-1)" ) ;
			return ;
		}
		
		AssignCluster.threshold = Double.valueOf( str[ 4 ] ) ;
		if ( str[0].equals( "all") ) 
			( new AssignCluster ( str[ 2 ])).generateAllDataSets( str[ 1 ], str [ 3 ] ) ;
		else if ( str[ 0 ].equals("one")) {
			String imageCorpus = str[ 1 ] + "\\ImageCorpus.txt" ; 
			String dstPath = str[ 1 ] + str [ 3 ] ;
			( new AssignCluster ( str[ 2 ])).generateOneDataSet( imageCorpus , dstPath, str[ 1 ] ) ;
		}
	}
	
	public void selfTest ( String matFile, String resultFile ) {
		try {
			Scanner matScanner = new Scanner ( new File ( matFile ) );
			Scanner resultScanner = new Scanner ( new File (  resultFile )) ;
			int vecs = matScanner.nextInt () ; matScanner.nextLine() ;
			int uncorrects = 0 ; 
			for (int i = 0 ; i < vecs ; i ++ ) {
				double vec [] = new double [ DIM ] ;
				for ( int j = 0 ; j < DIM; j++ ) vec[ j ] = matScanner.nextInt() ;
				int result = classify ( vec ) ;
				int correct = resultScanner.nextInt() ;
				if ( result !=  correct ) {
					System.out.println ( "wrong at vector " + i + ": correct result is " + correct 
							+ " and your result is " + result + "  diff: " +  cos_sim (center[correct], center[result])) ;
					uncorrects ++ ; 
				//	return ;
				}
			}
			System.out.println ( uncorrects / (double)vecs ) ;
			if ( uncorrects == 0 ) System.out.println ( "The program is correct in " + vecs + " vectors" )	; 
		}
		catch ( Exception e ) {
			e.printStackTrace () ;
		}
	}
	
	public AssignCluster ( String codeBook ) {
		try {
			Scanner bookScanner = new Scanner ( new File ( codeBook ) ) ;
			totalCenters = bookScanner.nextInt() ;
			DIM = bookScanner.nextInt() ; 
			center = new double [ totalCenters ][ DIM ] ;
			for ( int i = 0 ; i  < totalCenters ; i ++ ) 
				for ( int j = 0 ; j < DIM; j ++ ) 
					center [ i ][ j ] = bookScanner.nextDouble() ;
			bookScanner.close() ;
		}
		catch ( Exception e ) {
			e.printStackTrace() ;
		}		
	}
	
	public void generateAllDataSets ( String configFile, String outputfile ) throws Exception {
			Scanner scanner = new Scanner ( new File ( configFile ));
			int datasets = scanner.nextInt () ;
			scanner.nextLine () ; 
			for ( int i = 0 ; i < datasets ; i ++ ) {
				String path = scanner.nextLine () ;
				String imageCorpus = path + "\\ImageCorpus.txt" ; 
				String dstPath = path + outputfile ;
				System.out.println ( "Generating " + dstPath ) ; 
				generateOneDataSet ( imageCorpus , dstPath, path  ) ;
			}
	}
		
	public void generateOneDataSet ( String imageCorpus , String dstPath , String env )throws Exception {
		Vector < Node > corpus = new Vector < Node > () ;
		int maxClassNo = 0 ;
		Scanner scanner = new Scanner ( new File ( imageCorpus )); 
		while ( scanner.hasNextInt () ) {
			int classNo = scanner.nextInt() ;
			int wordNo = scanner.nextInt () ;
			String path = scanner.nextLine().trim() + ".pca" ;
//			String path = scanner.nextLine().trim() + ".sift_o" ;
			if ( path.indexOf(':')== -1 ) path = env + "\\" + path ;  
			if ( (new File(path)).exists() ) corpus.add( new Node ( classNo , wordNo, path ));
			if ( classNo > maxClassNo ) maxClassNo = classNo ; 
		}
		scanner.close() ;

		PrintStream fout = new PrintStream ( new File ( dstPath) );
		fout.println ( corpus.size() + " " + ( maxClassNo + 1 ) /2 ) ;
		for ( Node node : corpus ) {
			if ( ! (new File ( node.path ).exists() ) )continue ; 
			int [] result = generateFile ( node.path ) ;
			if ( result == null ) continue ; 
			fout.print ( node.classNo + " " + node.wordNo) ;
			for ( int i = 0 ; i < totalCenters ; i ++ ) 
				if ( result [ i ] !=0 ) fout.print ( " " + (i+1) + ":" + result [ i ] );
			fout.println ( ); 
		}
			fout.close() ;
	}	
	
	private int [] generateFile ( String imageFile ) throws Exception {
		int result [] = new int [ totalCenters ] ;
		Scanner scanner = new Scanner ( new File ( imageFile));
		double vec [] = new double [ DIM ] ; 
		int imageVec = scanner.nextInt() ; scanner.next() ;
		int count = 0 ;
		for ( int i = 0 ; i < imageVec ; i ++ ) {
			//for ( int j = 0 ; j < 4 ; j ++ ) scanner.next() ;
			for ( int j = 0 ; j < DIM ; j ++ ) vec [ j ] = scanner.nextDouble() ;

			int tmp = classify ( vec ) ;
			if ( tmp != -1 ) {
				result [ tmp ] ++ ; //= ratio ;
				count ++ ; 
			}
		}
		scanner.close() ;
		System.out.println ( imageFile + " " + ((double) count / imageVec ));
		return result ;		
	}
	
	double ratio = 0 ; 
	
	int classify ( double [] vec ) {
		double min1 = euclid ( center[ 0 ], vec ) ;
		double min2 = min1 * 2 ;  // all min2 >= min1
		int result = 0 ; 
		for ( int i = 1 ; i < totalCenters ; i ++ ) {
			double temp = euclid ( center [ i ] , vec ) ; 
			if ( temp < min1 ) {
				min2 = min1 ; 
				min1 = temp ;
				result = i ; 
			} else if ( temp < min2 ) min2 = temp ; 
		}
		ratio = min2 / min1 ;
		//ratio *= ratio ;
		return result ; 
//		if ( min1 < min2 * 0.49 ) System.out.printf ( "%.4f\t %.4f\t %.4f\r\n", min1, min2, min1/min2 ) ; 
//		if ( min1 < min2 * threshold ) return result ;
//		else return -1;
	}
	
//	public static void normalize ( double [] vec ) {
//		double total = 0 ; 
//		for ( int i = 0 ; i < vec.length ;i ++ ) total += vec [ i ] * vec [ i ] ;
//		total = Math.sqrt( total ) ;
//		for ( int i = 0 ; i < vec.length ; i ++ ) vec [ i ] /= total ; 
//	}
	
	double cos_sim ( double [] v1, double [] v2 ) {   // Cosine similarity, negitive it to be distance
		int min = Math.min(v1.length, v2.length);
		double result = 0 ;
		for ( int i = 0 ; i < min ; i ++ ) result += ( v1[ i ]* v2[ i ] ) ;
		return - result ;
	}

	double euclid ( double [] v1, double [] v2 ) {   // Cosine similarity, negitive it to be distance
		int min = Math.min(v1.length, v2.length);
		double result = 0 ;
		for ( int i = 0 ; i < min ; i ++ ) result += (( v1[ i ]- v2[ i ] )*( v1[ i ]- v2[ i ] ));
		return result ;
	}

	
	public void setThreshold ( double t ) {
		threshold = t ; 
	}
	
	class Node {
		int classNo ;
		int wordNo ;
		String path ; 
		public Node ( int classNo , int wordNo, String path) {
			this.classNo = classNo ;
			this.wordNo = wordNo ;
			this.path = path ; 
		}
	}
}
